#include "module_frame.hh"
#include "framework.hh"
#include "entity.hh"
#include "module_frame_http.hh"
#include "flatbuffers_helper.hh"
#include "entity_search_cache.hh"
#include "../protocol/protocol_header.hh"
#include "../protocol/locator/register_entity_req_generated.h"
#include "../protocol/locator/register_entity_ack_generated.h"
#include "../protocol/locator/unregister_entity_req_generated.h"
#include "../protocol/locator/entity_relay_msg_req_generated.h"
#include "../protocol/locator/entity_search_req_generated.h"

void ModuleFrame::setFramework(Framework * framework) {
    if (framework_) {
        return;
    }
    framework_ = framework;
    moduleType_ = framework_->onModuleType();
}

Framework * ModuleFrame::getFramework() {
    return framework_;
}

TimerHandle ModuleFrame::startTimer(std::time_t duration, std::uint64_t userData) {
    if (moduleType_ == ModuleType::SERVICE_MODULE) {
        return INVALID_TIMER_ID;
    }
    FRAMEWORK_CHECK_RETURN(!duration, INVALID_TIMER_ID, "Invalid parameter, startTimer");
    auto timerID = engine_->startTimer(duration);
    FRAMEWORK_CHECK_RETURN(timerID == INVALID_TIMER_ID, INVALID_TIMER_ID,
        "startTimer failed, duration:" << duration);
    timerMap_.emplace(timerID, TimerInfo{ nullptr, userData, false });
    return timerID;
}

TimerHandle ModuleFrame::startTimerOnce(std::time_t duration, std::uint64_t userData) {
    if (moduleType_ == ModuleType::SERVICE_MODULE) {
        return INVALID_TIMER_ID;
    }
    FRAMEWORK_CHECK_RETURN(!duration, INVALID_TIMER_ID, "Invalid parameter, startTimerOnce");
    auto timerID = engine_->startTimerOnce(duration);
    FRAMEWORK_CHECK_RETURN(timerID == INVALID_TIMER_ID, INVALID_TIMER_ID,
        "startTimerOnce failed, duration:" << duration);
    timerMap_.emplace(timerID, TimerInfo{ nullptr, userData, true });
    return timerID;
}

TimerHandle ModuleFrame::startTimer(std::time_t duration, TimerCallback cb, std::uint64_t userData) {
    if (moduleType_ == ModuleType::SERVICE_MODULE) {
        return INVALID_TIMER_ID;
    }
    FRAMEWORK_CHECK_RETURN(!duration, INVALID_TIMER_ID, "Invalid parameter, startTimer");
    auto timerID = engine_->startTimer(duration);
    FRAMEWORK_CHECK_RETURN(timerID == INVALID_TIMER_ID, INVALID_TIMER_ID,
        "startTimer failed, duration:" << duration);
    timerMap_.emplace(timerID, TimerInfo{ cb , userData, false });
    return timerID;
}

TimerHandle ModuleFrame::startTimerOnce(std::time_t duration, TimerCallback cb, std::uint64_t userData) {
    if (moduleType_ == ModuleType::SERVICE_MODULE) {
        return INVALID_TIMER_ID;
    }
    FRAMEWORK_CHECK_RETURN(!duration, INVALID_TIMER_ID, "Invalid parameter, startTimerOnce");
    auto timerID = engine_->startTimerOnce(duration);
    FRAMEWORK_CHECK_RETURN(timerID == INVALID_TIMER_ID, INVALID_TIMER_ID,
        "startTimerOnce failed, duration:" << duration);
    timerMap_.emplace(timerID, TimerInfo{ cb , userData, true });
    return timerID;
}

void ModuleFrame::cancelTimer(TimerHandle timerID) {
    if (moduleType_ == ModuleType::SERVICE_MODULE) {
        return;
    }
    if (INVALID_TIMER_ID == timerID) {
        return;
    }
    auto it = timerMap_.find(timerID);
    FRAMEWORK_CHECK(it == timerMap_.end(), "cancelTimer failed, timer ID:" << timerID);
    engine_->cancelTimer(timerID);
    // Timer info. MUST be removed in next frame
    cancelledTimers_.push_back(timerID);
}

khttp::HttpRequest * ModuleFrame::newHttpRequest(khttp::HttpCallback * cb, const std::string & host,
    const std::string & uri, int32_t port) {
    return engine_->newHttpRequest(cb, host, uri, port);
}

khttp::HttpRequest * ModuleFrame::newHttpRequest(HttpMethod cb, const std::string & host, const std::string & uri, int32_t port) {
    auto internalCb = kratos::corelib::allocate<InternalHttpCallback>(HttpCbType::HTTP_CB_CLIENT, cb, this);
    FRAMEWORK_CHECK_RETURN(!internalCb, nullptr, "HTTP internal callback malloc failed, 'internalCb'");
    auto request = newHttpRequest(internalCb, host, uri, port);
    if (!request) {
        errorlog << "HTTP internal callback malloc failed, 'newHttpRequest', host:" << host <<
            ", port:" << port << ", URI:" << uri << endlog;
        kratos::corelib::dispose(internalCb);
        return nullptr;
    }
    return request;
}

bool ModuleFrame::newHttpServer(khttp::HttpCallback * cb, const std::string & host, int32_t port) {
    return engine_->newHttpServer(cb, host, port);
}

bool ModuleFrame::newHttpServer(HttpMethod cb, const std::string & host, int32_t port) {
    auto internalCb = kratos::corelib::allocate<InternalHttpCallback>(HttpCbType::HTTP_CB_SERVER, cb, this);
    FRAMEWORK_CHECK_RETURN(!internalCb, false, "HTTP internal callback malloc failed, 'internalCb'");
    auto result = newHttpServer(internalCb, host, port);
    if (!result) {
        errorlog << "Start HTTP server failed, 'newHttpServer', host:" << host << ", port:" << port << endlog;
        kratos::corelib::dispose(internalCb);
    } else {
        httpServerList_.push_back(internalCb);
    }
    return result;
}

std::string ModuleFrame::doCommand(const std::string & moduleName, const std::string & command) {
    return engine_->doCommand(moduleName, command);
}

std::string ModuleFrame::doCommand(const std::string & command) {
    return engine_->doCommand(command);
}

Log& ModuleFrame::getLogverb() {
    return engine_->getLogverb();
}

Log& ModuleFrame::getLogdebug() {
    return engine_->getLogdebug();
}

Log& ModuleFrame::getLogwarn() {
    return engine_->getLogwarn();
}

Log& ModuleFrame::getLogerror() {
    return engine_->getLogerror();
}

Log& ModuleFrame::getLogfatal() {
    return engine_->getLogfatal();
}

const Finish& ModuleFrame::getLogend() {
    return engine_->getLogend();
}

ObjectPoolManager& ModuleFrame::getObjectPoolManager() {
    return engine_->getObjectPoolManager();
}

ComponentManager& ModuleFrame::getComponentManager() {
    return engine_->getComponentManager();
}

std::size_t ModuleFrame::getEngineMemoryPoolSize() {
    return engine_->getMemoryPoolSize();
}

std::uint64_t ModuleFrame::getUUID() {
    return engine_->getUUID();
}

std::uint64_t ModuleFrame::getUUIDRandom() {
    return engine_->getUUIDRandom();
}

PathID  ModuleFrame::getLocator(std::uint64_t entityID) {
    return engine_->getLocator(entityID);
}
